package mjava.lib;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;

import mjava.gc.AbstractMachine;

public class TdC implements TdS_itf, Serializable {

	private static final long serialVersionUID = -3498876807212119538L;

	// class name
	private String name;

	// father class
	private TdC superClass;

	// import class table
	private TdI tdi;

	// attribute  table
	private HashMap<String, Attribut> TdA;

	// method table
	private HashMap<String, Methode> TdM;

	// constructor table
	private HashMap<String, Constructeur> TdConst;

	private int taille;

	public TdC(TdI tdi) {
		this.tdi = tdi;
		this.TdA = new HashMap<String, Attribut>();
		this.TdM = new HashMap<String, Methode>();
		this.TdConst = new HashMap<String, Constructeur>();
	}

	public TdC(String name) throws FileNotFoundException {
		this.superClass = loadExtendsClass(name);
		this.TdA = new HashMap<String, Attribut>();
		this.TdM = new HashMap<String, Methode>();
		this.TdConst = new HashMap<String, Constructeur>();
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public TdC getSuperClass() {
		return superClass;
	}

	public HashMap<String, Constructeur> getTdConst() {
		return TdConst;
	}

	public void setSuperClass(TdC superClass) {
		this.superClass = superClass;
	}

	public HashMap<String, Attribut> getTdA() {
		return TdA;
	}


	public HashMap<String, Methode> getTdM() {
		return TdM;
	}

	public TdC getTdC() {
		return this;
	}
	
	public void addTdCDef(String name, DDEF def) {
		if(def instanceof Attribut) {
			((INFO)def).setIsGlobal(true);
			this.addAttrOrVar(name, (INFO)def);
		} else if (def instanceof Methode) {
			//((INFO)def).setIsGlobal(true);
			this.addMethod(name, (Methode)def);
		} else if (def instanceof Constructeur) {
			//((INFO)def).setIsGlobal(true);
			if(name.equals(this.name)) {
				this.addConstructor((Constructeur)def);
			} else {
				System.err.println("Constructor name error.");
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public void addMethod(String name, Methode methode) {
		TdM.put(name + methode.getParams().getParamsTypeNamesString(), methode);
		methode.setDeplacement(this.taille);
	}

	@SuppressWarnings("unchecked")
	public void addConstructor(Constructeur constructeur) {
		TdConst.put(this.name + constructeur.getParams().getParamsTypeNamesString(), constructeur);
		constructeur.setDeplacement(this.taille);
	}

	public void addAttrOrVar(String name, INFO attr) {
		this.TdA.put(name, (Attribut)attr);
		this.taille += attr.getTaille();
		boolean test = TdA.containsKey(name);
	}

	/*public INFO chercher(String n) {
		Attribut result = TdA.get(n);
		if (result != null){
			return result.getINFO();
		}
		return null;
	}*/

	@SuppressWarnings("unchecked")
	public Constructeur checkConstructor(String params, String name) {
		Constructeur c = null;
		if(this.name.equals(name)) {
			c = TdConst.get(name + params);
		} else {
			// cherche le constructeur dans le imports
			if(c == null) {
				Iterator it = this.tdi.keySet().iterator();
				TdC tdcTmp = null;
				while(it.hasNext()) {
					tdcTmp = this.tdi.get(it.next());
					c = tdcTmp.checkConstructor(params, name);
					if(c != null) break;
				}
			}
		}
		return c;
	}

	@SuppressWarnings("unchecked")
	public Methode checkMethod(String name, String params, String className) {
		
		Methode m = null;
		if(this.name.equals(className)) {
			m = TdM.get(name + params);
		} else {
			String cn = ((Attribut)this.TdA.get(className)).getType().getName();
			// cherche le constructeur dans le imports
			Iterator it = this.tdi.keySet().iterator();
			TdC tdcTmp = null;
			while(it.hasNext()) {
				tdcTmp = this.tdi.get(it.next());
				if(tdcTmp.getName().equals(cn)) {
					m = tdcTmp.checkMethod(name, params, cn);
					break;
				}
			}
		}
		return m;
	}

	public Attribut checkAttribut(String name) {
		Attribut attr = this.TdA.get(name);
		boolean test = this.TdA.containsKey(name);
		return attr;
	}

	public DTYPE getType() {
		return new DTYPE(this.name, this.taille);
	}

	public int getTaille() {
		return this.taille;
	}

	public TdC getImportedClass(String Name) {
		return tdi.get(name);
	}

	public INFO chercher(String n) {
		return this.checkAttribut(n);
	}
	
	public void sayHello(String hello){
		System.out.println(hello + "    "+ name);
	}

	public void RemoveParams(DPARAMS params) {
		// nothing to do
	}

	public void addParams(DPARAMS params) {
		// nothing to do
	}

	public int getDeplacement() {
		return this.getTaille();
	}

	public boolean varFromTdC(String name) {
		if(this.chercher(name) != null) {
			return true;
		} else {
			return false;
		}
	}
	
	public DTYPE searchType(String name) {
		DTYPE type = null;
		if(this.name.equals(name)) {
			type = new DTYPE(name, this.taille);
			return type;
		} else {
			TdC tmpTdC = this.tdi.get(name); 
			if(tmpTdC == null) {
				type = new DTYPE("error", 0);
				System.err.println("Type: " + name + " doesn't exist.");
			} else {
				type = new DTYPE(tmpTdC.getName(), tmpTdC.getTaille());
			}
			return type;
		}
	}

	public TdC loadExtendsClass(String className) throws FileNotFoundException{
		
		TdC tdc = null;
		try{
			FileInputStream fs = new FileInputStream(AbstractMachine.getPath() + className + ".tdc");
			ObjectInputStream os = new ObjectInputStream(fs);
			Object o = os.readObject();
			tdc = (TdC)o;
			os.close();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return tdc;
	}
}